<?php

// Namespace

namespace Loop;


/**
 * CNotificationManager allows to notify users by e-mail, SMS, etc.
 */

class CNotificationManager
{

	/**
	 * Send different kind of notifications to users 
	 *
	 * @return 	array 	An array which contains the handled exceptions during the notification process
	 */

	public function api_sendNotifications()
	{
		// Call the api

		$result = $this->sendNotifications();


		return $result;
	}


	/**
	 * Calls the extension which will be used to notify users
	 *
	 * @param 	string 	$extensionId 	The Id/name of the extension
	 * @param 	array 	$arguments 		An array which contains needed arguments
	 */

	public function callExtension($extensionId, $arguments = null)
	{
		// If notifications disabled, exit

		if (pref('[Loop]/features/notifications') == false)
		{
			return;
		}
	}


	/**
	 * Calls the url which will be used to notify users
	 *
	 * @param 	string 	$url 		The given url
	 * @param 	array 	$arguments 	An array which contains needed arguments
	 *
	 * @return 	string 	The result of the query
	 */

	public function callUrl($url, $arguments = null)
	{
		// If notifications disabled, exit

		if (pref('[Loop]/features/notifications') == false)
		{
			return;
		}


		// Call the URL

		$result = app()->getHttpHelper()->post
		(
			$url,
			array
			(
				'arguments' => json_encode($arguments)
			)
		);

		
		return $result;
	}


	/**
	 * Send a notification to users following the given arguments
	 *
	 * @param 	string 		$notificationType 		The type of notification to be sent
	 * @param 	string 		$extensionId 			The Id/name of the extension
	 * @param 	string 		$notificationName 		The name of the notification to be sent
	 * @param 	string 		$notificationTarget 	The target of the notification
	 * @param 	string 		$recipientName 			The name of the notification recipient
	 * @param 	mixed 		$notificationVariables 	The arguments that the notification may need
	 * @param 	boolean 	$isAsynchronous 		An option which allow to send the notification asynchronously if it is set to true (false by default)
	 * @param 	boolean 	$isUnique 				An option which prevent to send the notification twice
	 * @param 	mixed 		$keyFragments 			The elements needed to generate a unique key for the notification
	 */
	
	public function notify($notificationType, $extensionId, $notificationName, $notificationTarget, $recipientName, $notificationVariables = null, $isAsynchronous = false, $isUnique = false, $keyFragments = null)
	{
		// Dependencies

		require_once(PATH_LOOP . 'Model/CNotification.php');


		// Create a notification and a key

		$loopNotification = new \Loop\CNotification();


		// Record the notification

		if ($isAsynchronous === true)
		{
			$loopNotification->reset();

			$loopNotification->setExtensionId($extensionId);
			$loopNotification->setNotificationName($notificationName);
			$loopNotification->setNotificationTarget($notificationTarget);
			$loopNotification->setNotificationType($notificationType);		
			if(is_null($notificationVariables) === false)
			{
				$loopNotification->setNotificationVariables(json_encode($notificationVariables));
			}
			$loopNotification->setIsUnique($isUnique);
			$loopNotification->setRecipientName($recipientName);


			$loopNotification->save();	

			return;
		}


		// Create a key based on variables

		$key = md5(json_encode($keyFragments));


		// Should we prevent the notification from being sent twice?

		if ($isUnique === true)
		{
			// Encode the notification if it's an array

			if (is_array($notificationTarget) === true)
			{
				$notificationTarget = json_encode($notificationTarget);
			}


			// Find previous notifications

			$rawLoopNotifications = $loopNotification->find
			(
				array
				(
					'conditions' => array
					(
						'extensionId' => array(EQUAL, $extensionId),
						'key' => array(EQUAL, $key),
						'notificationName' => array(EQUAL, $notificationName),
						'notificationTarget' => array(EQUAL, $notificationTarget),
						'notificationType' => array(EQUAL, $notificationType)
					)
				)
			);


			// Do we have more than one?

			$nbLoopNotifications = count($rawLoopNotifications);

			if ($nbLoopNotifications >= 1)
			{
				e
				(
					EXCEPTION_NOTIFICATION_ALREADY_SENT,
					array
					(
						'extensionId' => $extensionId,
						'key' => $key,
						'nbNotifications' => $nbLoopNotifications,
						'notificationName' => $notificationName,
						'notificationTarget' => $notificationTarget,
						'notificationType' => $notificationType
					)
				);
			}
		}


		// Save the notification

		$loopNotification->setExtensionId($extensionId);
		$loopNotification->setNotificationName($notificationName);
		$loopNotification->setNotificationTarget($notificationTarget);
		$loopNotification->setNotificationType($notificationType);		
		$loopNotification->setKey($key);
		$loopNotification->setTimeOfExpedition(time());

		$loopNotification->save();


		// Send the right notification

		if ($notificationType == NOTIFICATION_EMAIL)
		{
			// Send an e-mail to the user

			$this->sendEmail
			(
				$extensionId,
				$notificationName,
				$notificationTarget,
				$recipientName,
				$notificationVariables
			);
		}
		else if ($notificationType == NOTIFICATION_EXTENSION)
		{
			$this->callExtension
			(
				$extensionId,
				$notificationName,
				$notificationTarget,
				$recipientName,
				$notificationVariables
			);
		}
		else if ($notificationType == NOTIFICATION_URL)
		{
			$this->callUrl($notificationTarget, $notificationVariables);
		}
		else
		{
			e
			(
				EXCEPTION_NOTIFICATION_TYPE_NOT_SUPPORTED,
				array
				(
					'notificationType' => $notificationType
				)
			);
		}	
	}
	
	
	/**
	 * Send an e-mail to the given recipient
	 *
	 * @param 	string 		$extensionId 			The Id/name of the extension
	 * @param 	string 		$notificationName 		The name of the file which will be used for the email content
	 * @param 	string 		$recipientEmail 		The email address of the recipient
	 * @param 	string 		$recipientName 			The email recipient name
	 * @param 	mixed 		$notificationVariables 	The arguments that the notification may need
	 */
	
	public function sendEmail($extensionId, $notificationName, $recipientEmail, $recipientName, $notificationVariables = null)
	{
		// Dependencies
		
		require_once(PATH_LOOP . 'Component/PHPMailer/5.2.1/class.phpmailer.php');


		// If notifications disabled, exit

		if (pref('[Loop]/features/notifications') == false)
		{
			return;
		}
		
		
		// Generate the HTML + TXT content

		$pathsToViews = array
		(
			'/[' . $extensionId . ']/Notification/Snippet.Header' => false,
			'/[' . $extensionId . ']/Notification/' . $notificationName => true,
			'/[' . $extensionId . ']/Notification/Snippet.Footer' => false
		);

		$universe = pref('[Loop]/application/universe');
		$viewsSuffixes = array('');

		if (empty($universe) == false)
		{
			$viewsSuffixes[] = $universe;
		}

		$filesExtensions = array
		(
			'html',
			'txt'
		);

		$viewContent = array
		(
			'html' => null,
			'txt' => null
		);

		foreach ($filesExtensions as $fileExtension)
		{
			$exceptions = array();

			foreach ($pathsToViews as $pathToView => $isViewRequired)
			{
				foreach ($viewsSuffixes as $viewSuffix)
				{
					if (empty($viewSuffix) === false)
					{
						$pathToView .= '_' . $universe;	
					}
					
					$pathToView .= '.email.' . $fileExtension;

					try
					{
						$viewContent[$fileExtension] .= render
						(
							$pathToView,
							$notificationVariables,
							false
						);
					}
					catch (\Exception $e)
					{
						if ($e->getCode() === EXCEPTION_VIEW_NOT_FOUND)
						{
							if ($isViewRequired == true)
							{
								$exceptions[] = $e;
							}
						}
						else
						{
							throw $e;
						}
					}
				}
			}

			if(count($exceptions) === count($viewsSuffixes))
			{
				$viewContent['html'] = null;
			}			
		}


		// Get the subject of the e-mail
		
		$subject = str('/Notification/' . $notificationName);

		
		// If in debug mode, send the message to the sender

		if (pref('[Loop]/notifications/sandbox') === true)
		{
			$subject .= ' - ' . $recipientName . ' <' . $recipientEmail . '>';
			$recipientEmail = pref('[Loop]/notifications/smtp/from');
		}


		// Setup the e-mailer

		$email = new \PHPMailer();
		
		$email->IsSMTP();
		
		$email->CharSet = 'UTF-8';
		$email->Host = pref('[Loop]/notifications/smtp/host');

		$authMode = pref('[Loop]/notifications/smtp/authMode');

		if (empty($authMode) === true)
		{
			$email->SMTPAuth = false;
			$email->SMTPAuthMode = null;
		}
		else
		{
			$email->SMTPAuth = true;
			$email->SMTPAuthMode = pref('[Loop]/notifications/smtp/authMode');
		}

		if (pref('[Loop]/notifications/smtp/isSecure') === true)
		{
			$email->SMTPSecure = 'ssl';
		}
		else
		{
			$email->SMTPSecure = null;
		}

		$email->Username = pref('[Loop]/notifications/smtp/login');
		$email->Password = pref('[Loop]/notifications/smtp/password');
		$email->Port = pref('[Loop]/notifications/smtp/port');

		$email->SetFrom(pref('[Loop]/notifications/smtp/from'), pref('[Loop]/application/name'));
		$email->AddAddress($recipientEmail, $recipientName);
		$email->Subject = $subject;

		if (empty($viewContent['txt']) == false)
		{

			$email->IsHTML(false);

			$email->Body = $viewContent['txt'];
			$email->AltBody = $viewContent['txt'];
		}

		if (empty($viewContent['html']) == false)
		{
			$email->IsHTML(true);
			
			$email->Body = $viewContent['html'];
			$email->AltBody = $viewContent['txt'];
		}

		// Should we send a blind carbon-copy?

		$bccEmails = pref('[Loop]/notifications/smtp/bcc');

		if (is_array($bccEmails) == true)
		{
			foreach ($bccEmails as $bccEmail)
			{
				$email->AddBCC($bccEmail, $bccEmail);
			}
		}


		// Send the email
		
		if ($email->Send() == false)
		{
			e
			(
				EXCEPTION_NOTIFICATION_FAILED,
				array
				(
					'error' => $email->ErrorInfo
				)
			);
		}
	}


	/**
	 * Check if there's any pending notification, in this case send them
	 *
	 * @return 	array 	An array which contains the handled exceptions during the notification process
	 */

	public function sendNotifications()
	{
		$resultExceptions = array();

		// Dependencies

		require_once(PATH_LOOP . 'Model/CNotification.php');


		// Create objects

		$notification = new \Loop\CNotification();


		// Find the notification which have not been sent


		$rawNotifications = $notification->find 
		(
			array
			(
				'conditions' => array
				(
					'timeOfExpedition' => array(EQUAL, IS_NULL),
				)
			)
		);


		foreach ($rawNotifications as $rawNotification)
		{
			// Load the object

			$notification->loadFromRawObject($rawNotification);

			$notificationVariables = json_decode($notification->getNotificationVariables());


			// 	Notify

			try
			{
				notify
				(
					$notification->getNotificationType(), 
					$notification->getExtensionId(), 
					$notification->getNotificationName(), 
					$notification->getNotificationTarget(), 
					null, 
					$notificationVariables, 
					false, 
					$notification->getIsUnique()
				);

				$notification->delete();
			}
			catch(\Exception $e)
			{
				$resultExceptions[] = array 
				(
					$e->getCode(),
					array 
					(
						'notificationId' => $notification->getId()
					)
				);
			}
		}

		return $resultExceptions;
	}
}

?>
